import { Injectable, Request } from '@nestjs/common';
import { Response } from 'express';
import { InjectRepository } from '@nestjs/typeorm';
import { BaseLogEntity } from '@/entity/base-log.entity';
import { Repository } from 'typeorm';
import { ErrorEntity } from '@/entity/error.entity';
import { PaintEntity } from '@/entity/paint.entity';
import { TimingEntity } from '@/entity/timing.entity';
import { generatorErrorHash } from '@/utils';
import { LongTaskEntity } from '@/entity/long-task.entity';
import { parseSourceMap } from '@/utils/parse-source-map';

@Injectable()
export class SendService {
  constructor(
    @InjectRepository(BaseLogEntity)
    private baseLogRepository: Repository<BaseLogEntity>,
    @InjectRepository(ErrorEntity)
    private errorRepository: Repository<ErrorEntity>,
    @InjectRepository(PaintEntity)
    private paintRepository: Repository<PaintEntity>,
    @InjectRepository(TimingEntity)
    private timingRepository: Repository<TimingEntity>,
    @InjectRepository(LongTaskEntity)
    private longTaskRepository: Repository<LongTaskEntity>,
  ) {}

  /**
   * 接收前端发送的数据，保存基础数据
   * @param req
   * @param res
   */
  getMonitorGif(req: Request, res: Response) {
    const data = JSON.parse(decodeURIComponent(req.url.split('=')[1]));
    const {
      type,
      baseLog,
      DOMContentLoadedTime,
      loadTime,
      FP,
      FCP,
      LCP,
      FMP,
      isWhiteScreen,
      errorType,
      message,
      filename,
      position,
      reqUrl,
      stack,
      tagName,
      selector,
      xhrData,
      startTime,
      duration,
      eventType,
    } = data;
    const { title, url, userAgent, browser, os } = baseLog;
    this.baseLogRepository
      .save({
        title,
        url,
        type,
        userAgent,
        browser,
        os,
      })
      .then((baseLog) => {
        const { id: baseLogId } = baseLog;
        if (type === 'timing') {
          this.timingRepository.save({
            baseLogId,
            domContentLoadedTime: DOMContentLoadedTime,
            loadTime,
          });
        }
        if (type === 'paint') {
          if (FP || FCP || LCP || FMP) {
            this.paintRepository.save({
              baseLogId,
              fp: FP,
              fcp: FCP,
              lcp: LCP,
              fmp: FMP,
            });
          }
        }
        if (type === 'error') {
          const hashErrorId = generatorErrorHash(errorType, reqUrl, message, stack);
          this.errorRepository
            .findBy({
              hashErrorId,
            })
            .then(async (error) => {
              let total = 1;
              if (error.length > 0) {
                total = error[0].total + 1;
                this.errorRepository.update(error[0].id, {
                  total,
                });
              } else {
                let source;
                let line;
                let column;
                if (position) {
                  const { source: fileSource, line: lineNum, column: columnNum } = await parseSourceMap(filename.split('/').pop(), position);
                  source = fileSource;
                  line = lineNum;
                  column = columnNum;
                }
                this.errorRepository.save({
                  baseLogId,
                  hashErrorId,
                  total,
                  errorType,
                  message,
                  filename,
                  fileSource: source?.toString(),
                  tagName,
                  position: position ? `${line}:${column}` : '',
                  stack,
                  reqUrl,
                  selector,
                  isWhiteScreen: isWhiteScreen ? 1 : 0,
                  xhrData: xhrData ? JSON.stringify(xhrData) : '',
                });
              }
            });
        }
        if (type === 'longTask') {
          this.longTaskRepository.save({
            baseLogId,
            startTime,
            duration,
            eventType,
            selector,
          });
        }
      });
    const imgBuffer = Buffer.from(
      'R0lGODlhAQABAIAAAP///wAAACH5BAEAAAAALAAAAAABAAEAAAICRAEAOw==',
      'base64',
    );
    res.set('Content-Type', 'image/gif');
    res.send(imgBuffer);
  }

  /**
   * 获取类型总数
   */
  getTypeTotal() {
    return this.baseLogRepository
      .createQueryBuilder()
      .select('type, count(id) as total')
      .groupBy('type')
      .getRawMany();
  }

  /**
   * 获取错误信息统计
   */
  getErrorTotal() {
    return this.errorRepository
      .createQueryBuilder()
      .select('error_type as errorType, count(total) as total')
      .groupBy('errorType')
      .getRawMany();
  }

  /**
   * 获取长任务信息统计
   */
  getLongTaskTotal() {
    return this.longTaskRepository
      .createQueryBuilder()
      .select('event_type as eventType, count(id) as total')
      .where('event_type is not null')
      .groupBy('eventType')
      .getRawMany();
  }

  /**
   * 获取性能信息统计
   */
  async getPerformanceTotal() {
    const fpList = await this.paintRepository
      .createQueryBuilder()
      .select(
        'coalesce(fp, 0) as fp, gmt_create as gmtCreate, base_log_id as baseLogId',
      )
      .getRawMany();
    const fcpList = await this.paintRepository
      .createQueryBuilder()
      .select(
        'coalesce(fcp, 0) as fcp, gmt_create as gmtCreate, base_log_id as baseLogId',
      )
      .getRawMany();
    const lcpList = await this.paintRepository
      .createQueryBuilder()
      .select(
        'coalesce(lcp, 0) as lcp, gmt_create as gmtCreate, base_log_id as baseLogId',
      )
      .getRawMany();
    const fmpList = await this.paintRepository
      .createQueryBuilder()
      .select(
        'coalesce(fmp, 0) as fmp, gmt_create as gmtCreate, base_log_id as baseLogId',
      )
      .getRawMany();
    return {
      fpList,
      fcpList,
      lcpList,
      fmpList,
    };
  }

  /**
   * 获取性能时间信息统计
   */
  getTimingTotal() {
    return this.timingRepository
      .createQueryBuilder()
      .select(
        'load_time as loadTime, gmt_create as gmtCreate, base_log_id as baseLogId',
      )
      .getRawMany();
  }
}
